feat(core): framework-agnostic engine layer (PR 1/3)#137
Draft
itay-dar-lmnd wants to merge 17 commits into
Draft
feat(core): framework-agnostic engine layer (PR 1/3)#137itay-dar-lmnd wants to merge 17 commits into
itay-dar-lmnd wants to merge 17 commits into
Conversation
…point alias Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…d identity assertion for HttpMethod
…riptor Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…ctHttpAdapter Rename _PyNestABCMeta to _ABCMetaMROFix and reduce its scope to only the minimal Python 3.9 ABCMeta MRO fix; keep MRO-walk in __init__ which is required for mixin-style subclasses where the concrete provider appears after AbstractHttpAdapter in the MRO.
… clean test design Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…tion aliases Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…rized suite Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…ntracts, guards, filters, lifecycle, DI Bugs discovered during smoke testing (to fix in PR 3): 1. from __future__ import annotations in controllers breaks @Body/@query param binding — Pydantic v2 TypeAdapter receives ForwardRef instead of resolved type. Workaround: drop future annotations from controller files. 2. Route collector registers paths correctly but test URLs must omit trailing slash (FastAPI redirect_slashes=True default redirects /users/ -> /users).
…ter wrappers Three related fixes in nest/common/decorators.py and route_resolver.py: 1. wrap_param_decorators: call typing.get_type_hints(endpoint) to resolve string annotations from 'from __future__ import annotations' before building FastAPI Depends() signatures. Propagate resolved return annotation and __annotations__ onto the wrapper so FastAPI's response model serialization also works. 2. _wrap_with_filters: propagate resolved __annotations__ from the original endpoint onto filter_wrapper for the same reason. 3. _apply_pipes: catch ValueError/TypeError from pipe.transform() and re-raise as HTTP 422, preventing raw exceptions from propagating through the ASGI stack. test-app: expand from 23 to 61 tests across 6 modules, 3 compat tests, 5 stress tests New modules: - catalog: nested Pydantic models, variant stock management, cross-module export - pipeline: custom param decorators (createParamDecorator), pipes (TrimPipe, ClampPipe, PositiveIntPipe), async endpoints, Req()/Ip() injection - auth: cross-module DI (AuthService injects CatalogService), multi-guard (BearerGuard + AdminGuard), token management via shared TokenStore provider Stress tests: 50 concurrent reads, 30 concurrent async computes, 100 rapid fire writes+reads, 200-variant large payload, 200-req throughput baseline.
- Remove test-app/ from the uv workspace members so it's no longer part of the published package or git history going forward. - Untrack all test-app/ files via 'git rm -r --cached' (local copies preserved for continued use during PR-1 development). - Add test-app/ to .gitignore. The PR-1 engine contracts and the fix to nest/common/decorators.py + route_resolver.py remain unchanged. Full test suite still passes (188 tests).
PR 2 — nest/engines/fastapi/ FastAPIAdapter:
- Full AbstractHttpAdapter implementation (18 abstract methods)
- ParamSpec → FastAPI Depends/Body/Query/Path/Header translation
- ExceptionFilter wrapping, startup/shutdown hook routing, CORS, WS routes
- Constructor accepts FastAPI kwargs OR an existing FastAPI instance
PR 3 — cutover from direct FastAPI calls to adapter routing:
- PyNestFactory.create accepts adapter= parameter (default FastAPIAdapter)
- PyNestApp holds adapter; backward-compat shims for .http_server, .use, etc.
- RoutesResolver emits RouteSpec and calls adapter.add_route(spec)
- nest/common/decorators.py is now engine-neutral (returns ParamSpec); the
FastAPI binding logic lives in nest/engines/fastapi/params.py
- HttpMethod canonical location moved to nest.engine.types; the old
nest.core.decorators.http_method.HTTPMethod re-exports it
- Constructors accept either adapter or raw FastAPI for 0.6 compatibility
PR 4 — conformance suite + 4 doc pages:
- 17 conformance tests covering route registration, param binding, request
accessors, middleware, exception handlers, lifespan, CORS, WebSockets
- docs/engine/{overview,fastapi_adapter,writing_an_adapter,migration_0.7}.md
- mkdocs.yml updated with new Engine section
Verification:
- 218 root tests pass (up from 188 baseline; new engine tests included)
- 77 test-app tests pass — including 9 SQLite DB tests, 4 WebSocket gateway
tests, and 3 cutover assertions that verify FastAPIAdapter is in the loop
- All 5 stress tests still pass (50 concurrent reads, 30 concurrent async
computes, 100 rapid-fire writes+reads, 200-variant payload, 200-req
throughput baseline)
- Zero behavioral edits to existing test files
Phase-1 known coupling (documented in docs/engine/fastapi_adapter.md):
- Guards with security_scheme remain FastAPI-only until phase 2
- WebSocket gateways remain FastAPI-only until phase 2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a framework-agnostic engine layer so PyNest is no longer hard-wired to FastAPI. Routing, parameter binding, lifecycle, exception handling, middleware, CORS and WebSockets are now expressed against a neutral contract (
AbstractHttpAdapter), with FastAPI reimplemented as the first adapter behind it. No user-facing API changes — existing apps keep working.This is PR 1 of 3 in a stack:
What's inside
Neutral engine contract (
nest/engine/)http_adapter.py—AbstractHttpAdapter, the NestJS-inspired engine contract every framework implementsroute_spec.py,params.py,execution_context.py,types.py— framework-neutral descriptors (RouteSpec,ParamSpec,ExecutionContext,HttpMethod)nest/http/— a stable facade (Request/Response/Depends/HTTPException) so app code imports from PyNest, not the underlying frameworkFastAPI as the first adapter (
nest/engines/fastapi/)adapter.py+params.py— the existing FastAPI behavior, now living behindAbstractHttpAdapterpynest_application.py,pynest_factory.py,route_resolver.py, decorators) rewired to drive the adapter instead of calling FastAPI directlyConformance test suite (
tests/test_engine/)Docs (
docs/engine/)Compatibility
No breaking changes for app authors. FastAPI remains the default engine; the abstraction sits underneath.